#include <length.h>

#include <MutexLock.h>
#include <MC.h>
#include <MM/MM.h>
#include <PAL/PAL.h>
#include "LibMemory.h"
using namespace StaticMemory;

#define PAGE_SIZE  (1 << 12)
#define PAGE_MASK  ((~0) << 12)



#define _KMalloc(size)  Kmalloc((_mch*)&kmemory,size)
#define _KFree(ptr)     Kfree((_mch*)&kmemory,ptr)

static inline _ptlong* Lib_Same_Process_Vaddr(_libs* lib,_pcb* pro)
{
  auto node = lib->stmhandle->pro_addr;
  while(node != NULL)
    {
      if((_pcb*)(node->mark[0]) == pro)
	return (_ptlong*)(node->mark[1]);
      node = node->next;
    }
  return (_ptlong*)1;

}
static inline int Lib_Same_Process_Check(_libs* lib,_pcb* pro)
{
  auto node = lib->stmhandle->pro_addr;
  while(node != NULL)
    {
      if((_pcb*)(node->mark[0]) == pro)
	return 0;
      node = node->next;
    }
  return 1;

}


using namespace LibMemory;


 
  _libs* CreateLibMemory(_pcb* pro,_ptlong* vAddr,_hfptlong pagenum,_ptlong flag)
  {//Makelength has some problem -- i have to reload operate + - * / of length
    _libs* newlib = (_libs*)_KMalloc(sizeof(_libs));
    *newlib = {NULL,0,pagenum,pagenum * PAGE_SIZE,[&](_ptlong vaddr)
		     {
		       stminfo* stmhandle = StaticMemorize(pro,(_ptlong*)vaddr,NULL,NULL);vaddr += PAGE_SIZE;
		       for(_ptlong i = 1 ; i < pagenum ; i++,vaddr += PAGE_SIZE)
			 StaticMemorize(pro,(_ptlong*)vaddr,NULL,NULL); // BIG PROBLEM
		       return stmhandle;
		       //Repeated Save. we have 'pagenum^2' number of stminfo. we can only save pagenum number of info, and calculate others address. 
		       //Problem is big.
		     }((_ptlong)vAddr),{NULL},0,NULL,NULL,1};
    return newlib;
  }
  int ShareLibMemory(_pcb* pro,_ptlong* vAddr,_libs* lib)
  {
    MUTEX_LOCK(&(lib->locker));
    //Not permitting one process has several same lib.
    if(Lib_Same_Process_Check(lib,pro) == 0)
      return 0;
    if(lib->upgrade != NULL)
      return ShareLibMemory(pro,vAddr,lib->upgrade);
    _ptlong vaddr = vAddr;
    for(_ptlong i = 0 ; i < lib->pagenum ; i++,vaddr += PAGE_SIZE)
      StaticMemorize((_pcb*)(lib->stmhandle->pro_addr->mark[0]),(_ptlong*)(lib->stmhandle->pro_addr->mark[1]),pro,(_ptlong*)vaddr);
    MUTEX_UNLOCK(&(lib->locker));
    return 1;

  }
  
  _libs* UpgradeLibMemory(_libs* lib, _libs* upgrade)
  {
    lib->upgrade = upgrade;
    return lib;
  }
  
  int RemoveLibMemory(_libs* lib,_pcb* pro)
  {
    MUTEX_LOCK(&(lib->locker));
    _ptlong vaddr = Lib_Same_Process_Vaddr(lib,pro);
    if((_ptlong*)vaddr == NULL) return 0;
    int i;
    for(_ptlong i = 0 ; i < lib->pagenum ; i++,vaddr += PAGE_SIZE)
      i = inStaticMemorize(pro,(_ptlong*)vaddr);
    
    MUTEX_UNLOCK(&(lib->locker));
    if(i == 2)
      {//Delete the Lib
	if(lib -> functbl != NULL)
	  _KFree(lib->functbl);
	_KFree(lib->stmhandle);
	_KFree(lib);
      }
    return i;
  }
  inline _libs* FileStreamToLibMemory(_fstr stream,_pcb* pro,_ptlong* vAddr,_length num,_length offset)
  {//BigProblem -- stmhandle* only save the first block's address of per process 
    if(stream->lib != NULL)
      return (ShareLibMemory(pro,vAddr,stream->lib),stream->lib);
    _libs* newlib = (_libs*)_KMalloc(sizeof(_libs));
    _str buff = CreateStream([](_ptlong)->_str{return ((_str)(0));});
    DelegateOnThread((_tcb*)ConvertToPub(stream,buff,num,offset),NOWT);
    
    _ptlong i = 1;_length I = PAGE_SIZE;
    _ptlong vaddr = vAddr;
    stminfo* handle = StaticMemorize(pro,(_ptlong*)vaddr,NULL,NULL);
    vaddr+= PAGE_SIZE;
    for(;I < num;i++, buff = SeekStream(buff,PAGE_SIZE),vaddr += PAGE_SIZE, I += PAGE_SIZE)
      {
	_ptlong* phyaddr;
	if((phyaddr = MMUGetPMem((_ptlong*)(pro->flPage),(_ptlong*)vaddr)) != NULL)
	  PushAbandenedMemory(phyaddr);
	LockMemory(pro,phyaddr = MMUGetPMem((_ptlong*)(pro->flPage),(_ptlong*)buff));
	MMUMemoryMap((_ptlong*)(pro->flPage),phyaddr,(_ptlong*)vaddr,0);
	MMUMemorySet((_ptlong*)(pro->flPage),vaddr,_PAGE_UNALLOCATED);
	StaticMemorize(pro,(_ptlong*)vaddr,NULL,NULL);
	UnlockMemory(pro,phyaddr);
      }
    EndStream(buff);
    *newlib = 
      {
	NULL,0,i,num,handle,{NULL},0,stream,NULL,1
      };
    stream->lib = newlib;
    return newlib;
  }
  //MapStream is equal to this
  _fstr LibMemoryToFileStream(_pcb* pro,_libs* lib)//Use copy-on-write
  {
    MUTEX_LOCK(&(lib->locker));
    if(lib->file == NULL)
      lib->file = CreateStream([](_ptlong size)->_fstr{return (_fstr)_KMalloc(size);});
    _str buff = CreateStream([](_ptlong)->_str{});
    //_str abuff = buff;
    ConvertToPrv(buff,lib->file,lib->size,0);
	_length offset = 0;
    for(_ptlong i = 0; 
	i<lib->pagenum; 
	i ++, buff = SeekStream(buff,PAGE_SIZE),offset += PAGE_SIZE)
      {
	CopyOnWrite(pro,buff,lib->stmhandle->phyMem);
      }
    MUTEX_UNLOCK(&(lib->locker));
    return lib->file;
  }
  //UnmapStream is equal to this
  _libs* SetFlagLibMemory(_libs* lib, _ptlong flag)
  {
    MUTEX_LOCK(&(lib->locker));

    MUTEX_UNLOCK(&(lib->locker));
  }
  
  _libs* SetFstrLibMemory(_libs* lib,_fstr target)
  {
    MUTEX_LOCK(&(lib->locker));
    if(lib->file != NULL)
      lib->file->lib = NULL;
    lib->file = target;
    MUTEX_UNLOCK(&(lib->locker));
    return lib;
  }
  
  
  


static _ptlong* AnalyzeFuncTbl(_libs* lib)
{
  
}




